using System;
using System.Collections.Generic;

namespace Fadd.Parser
{
    /// <summary>
    /// Tokens are used to parse a specific part of the text.
    /// </summary>
    public abstract class ParseTreeToken
    {
        private readonly List<ParseTreeToken> _children = new List<ParseTreeToken>();
        private ParseTreeToken _parent;

        /// <summary>
        /// Get parent token
        /// </summary>
        public ParseTreeToken Parent
        {
            get { return _parent; }
            internal set { _parent = value; }
        }

        /// <summary>
        /// This token is the final child in the current leaf, parse no more children
        /// </summary>
        public virtual bool FinalChild
        {
            get { return false; }
        }

        /// <summary>
        /// Check if the current position is the start of this token
        /// </summary>
        /// <returns>true if our token is the next one.</returns>
        public abstract bool Match(Tokenizer tokenizer, ParseTree tree);

        /// <summary>
        /// Parses the data in the specified <see cref="Tokenizer"/>.
        /// </summary>
        /// <param name="tokenizer">The <see cref="Tokenizer"/> containing the data to parse.</param>
        /// <param name="tree"><see cref="ParseTree"/> that is being built..</param>
        /// <returns>true if more tokens can be parsed on the current leaf; false if we should continue to next leaf (parse no more children).</returns>
        public abstract bool Parse(Tokenizer tokenizer, ParseTree tree);

                /// <summary>
        /// Should be called in <see cref="Parse"/> to let the Token parse all children.
        /// </summary>
        /// <param name="tokenizer">The <see cref="Tokenizer"/> containing the data to parse.</param>
        /// <param name="tree"><see cref="ParseTree"/> that is being built..</param>
        /// <returns>true if a child have been matched and parsed.</returns>
        /// <remarks>Are called from <see cref="Parse"/>.</remarks>
        public virtual bool ParseChildren(Tokenizer tokenizer, ParseTree tree)
        {
            bool haveParsed = ParseOneChild(tokenizer, tree);
            bool canRun = true;
            while (canRun)
                canRun = ParseOneChild(tokenizer, tree);

            return haveParsed;
        }

        /// <summary>
        /// Should be called in <see cref="Parse"/> to let the Token parse all children.
        /// </summary>
        /// <param name="tokenizer">The <see cref="Tokenizer"/> containing the data to parse.</param>
        /// <param name="tree"><see cref="ParseTree"/> that is being built..</param>
        /// <returns>true if a child have been matched and parsed.</returns>
        /// <remarks>Are called from <see cref="Parse"/>.</remarks>
        internal bool ParseOneChild(Tokenizer tokenizer, ParseTree tree)
        {
            foreach (ParseTreeToken token in _children)
            {
                if (tokenizer.PeekEOF)
                    return false;

                if (!token.Match(tokenizer, tree)) 
                    continue;

                // returns false if no more children should be parsed.
                return token.Parse(tokenizer, tree);
            }

            return false;
        }

        /// <summary>
        /// Used to check if another child got a match
        /// </summary>
        /// <param name="tokenizer"><see cref="Tokenizer"/> used to read data.</param>
        /// <param name="tree"><see cref="ParseTree"/> being built.</param>
        /// <param name="exceptMe"><see cref="ParseTreeToken"/> that should not be included in the check.</param>
        /// <returns>true if a child matches the current position in the <see cref="Tokenizer"/></returns>
        public bool MatchChildren(Tokenizer tokenizer, ParseTree tree, Type exceptMe)
        {
            foreach (ParseTreeToken child in _children)
            {
                if (exceptMe.IsAssignableFrom(child.GetType()))
                    continue;
                if (child.Match(tokenizer, tree))
                    return true;

            }

            return false;
        }

        /// <summary>
        /// Add a  possible child token.
        /// </summary>
        /// <param name="parseTreeToken">The token.</param>
        public void Add(ParseTreeToken parseTreeToken)
        {
            parseTreeToken.Parent = this;
            _children.Add(parseTreeToken);
        }
    }
}
